﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WJP.Logging
{
    /// <summary>
    /// <see cref="EventLog"/>Windows事件日志记录器
    /// </summary>
    [Serializable]
    public class EventLogLogger : AbstractLogger, IDisposable
    {
        private EventLog eventLog;

        /// <summary>
        /// 创建一个基于<see cref="EventLog"/>的日志记录器
        /// </summary>
        /// <param name="logName"></param>
        public EventLogLogger(string logName) : this(logName, "default")
        {

        }

        /// <summary>
        /// 创建一个基于<see cref="EventLog"/>的日志记录器
        /// </summary>
        /// <param name="logName"></param>
        /// <param name="source"></param>
        public EventLogLogger(string logName, string source) : base(LogLevel.Debug)
        {
            if (!EventLog.SourceExists(source))
                EventLog.CreateEventSource(source, logName);

            eventLog = new EventLog(logName)
            {
                Source = source
            };
        }

        /// <summary>
        /// 创建一个基于<see cref="EventLog"/>的日志记录器
        /// </summary>
        /// <param name="logName"></param>
        /// <param name="machineName"></param>
        /// <param name="source"></param>
        public EventLogLogger(string logName, string machineName, string source)
        {
            if (!EventLog.SourceExists(source, machineName))
            {
                var eventSourceCreationData = new EventSourceCreationData(source, logName)
                {
                    MachineName = machineName
                };
                EventLog.CreateEventSource(eventSourceCreationData);
            }

            eventLog = new EventLog(logName, machineName, source);
        }

        public override ILogger CreateChildLogger(string loggerName)
        {
            if(loggerName.IsNullOrEmpty())
                throw new ArgumentNullException(nameof(loggerName), "子日志记录器的名称不可为空");

            return new EventLogLogger(eventLog.Log, eventLog.MachineName, eventLog.Source);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private static EventLogEntryType TranslateLevel(LogLevel level)
        {
            switch (level)
            {
                case LogLevel.Error:
                case LogLevel.Fatal:
                    return EventLogEntryType.Error;
                case LogLevel.Warn:
                    return EventLogEntryType.Warning;
                default:
                    return EventLogEntryType.Information;
            }
        }

        protected override void Log(LogLevel loggerLevel, string loggerName, string message, Exception exception)
        {
            if (eventLog == null)
            {
                return;
            }

            var type = TranslateLevel(loggerLevel);

            string contentToLog;

            if (exception == null)
            {
                contentToLog = "[{0}] '{1}' message: {2}".FormatArgs(loggerLevel, loggerName, message);
            }
            else
            {
                contentToLog = "[{0}] '{1}' message: {2} exception: {3} {4} {5}"
                               .FormatArgs(loggerLevel, 
                                           loggerName, 
                                           message, 
                                           exception.GetType(), 
                                           exception.Message,
                                           exception.StackTrace);
            }

            eventLog.WriteEntry(contentToLog, type);
        }

        ~EventLogLogger()
        {
            Dispose(false);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (eventLog != null)
                {
                    eventLog.Close();
                    eventLog = null;
                }
            }
        }
    }
}
